package com.mrx.xfriendship.service;

import lombok.SneakyThrows;
import org.lionsoul.ip2region.xdb.Searcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;

import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class IPSearcherService {

    private static final Logger logger = LoggerFactory.getLogger(IPSearcherService.class);

    private final Searcher searcher;

    @SneakyThrows
    public IPSearcherService() {
        // 1、从 dbPath 加载整个 xdb 到内存。
        InputStream inputStream = IPSearcherService.class.getResourceAsStream("/data/ip2region.xdb");
        // 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。
        assert inputStream != null;
        searcher = Searcher.newWithBuffer(inputStream.readAllBytes());
        inputStream.close();
    }

    public String search(String ip) {
        // 3、查询
        long sTime = System.nanoTime();
        String region;
        try {
            String search = searcher.search(ip);
            logger.debug("searchResult: {}", search);
            List<String> list = Arrays.stream(search.split("\\|"))
                    .filter(it -> !"0".equals(it)).limit(2)
                    .collect(Collectors.toList());
            region = list.size() == 2 ? list.get(1) : list.get(0);
        } catch (Exception e) {
            region = "-";
        }
        long cost = TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - sTime);
        logger.debug("region: {}, ioCount: {}, took: {} μs", region, searcher.getIOCount(), cost);
        return region;
    }

    @SneakyThrows
    @EventListener(ContextClosedEvent.class)
    public void onApplicationEvent() {
        logger.debug("关闭 searcher");
        // 4、关闭资源 - 该 searcher 对象可以安全用于并发，等整个服务关闭的时候再关闭 searcher
        searcher.close();
        // 备注：并发使用，用整个 xdb 数据缓存创建的查询对象可以安全的用于并发，也就是你可以把这个 searcher 对象做成全局对象去跨线程访问。
    }

}